﻿using Model.CommonEntities;
using Model.StrategyEntities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static Model.StrategyEntities.LinearRegressionResult;

namespace BLL
{
    public sealed partial class AnalysisEngine
    {
        /// <summary>
        /// 对一组点通过最小二乘法进行线性回归
        /// </summary>
        /// <param name="parray"></param>
        public static List<decimal> LinearRegression(List<Point> PointList, int period = 5)
        {
            ////计算均值时，取值向前偏移的长度，最长要计算shift+1日均值，所以要向前再取shift个值
            //int shift = 4;
            ////实际取值长度 = 计算周期长度 + 向前偏移长度
            //int period_shift = period + shift;

            //for (int i = 0; i < period_shift; i++)
            //{
            //    if (i < period)
            //    {
            //        TList.Add(Convert.ToDecimal(stocklist.Keys.ToList<string>()[stocklist.Count - i - 1]));
            //        CloseList.Add(stocklist[Convert.ToString(stocklist.Keys.ToList<string>()[stocklist.Count - i - 1])].close);
            //        OpenList.Add(stocklist[Convert.ToString(stocklist.Keys.ToList<string>()[stocklist.Count - i - 1])].open);
            //        HighList.Add(stocklist[Convert.ToString(stocklist.Keys.ToList<string>()[stocklist.Count - i - 1])].high);
            //        LowList.Add(stocklist[Convert.ToString(stocklist.Keys.ToList<string>()[stocklist.Count - i - 1])].low);
            //    }
            //    //仅对待求滞后值的数据，取额外的偏移值
            //    PointList.Add(new Point(stocklist[Convert.ToString(stocklist.Keys.ToList<string>()[stocklist.Count - i - 1])].close, i));
            //}


            //PointList.Reverse();
            List<decimal> SlopeList = new List<decimal>();
            ////每次传入shift+1个点，判断最近shift+1日的斜率
            //shift += 1;

            //计算period天内的线性回归的斜率
            for (int i = 0; i < PointList.Count - period + 1; i++)
            {
                //截取向前shift天的数据
                var templist = PointList.GetRange(i, period);
                SlopeList.Add(LinearRegressionFunction(templist));
            }

            
            //CloseList.Reverse();
            //OpenList.Reverse();
            //HighList.Reverse();
            //LowList.Reverse();
            //TList.Reverse();

            //var result = new LinearRegressionResult
            //{
            //    TList = TList,
            //    CloseList = CloseList,
            //    OpenList = OpenList,
            //    HighList = HighList,
            //    LowList = LowList,
            //    SlopeList = SlopeList
            //};

            return SlopeList;
        }

        /// <summary>
        /// 返回线性回归的斜率
        /// </summary>
        /// <param name="parray"></param>
        /// <returns></returns>
        private static decimal LinearRegressionFunction(List<Point> parray)
        {
            string txt = string.Empty;
            //点数不能小于2
            if (parray.Count < 2)
            {
                txt += "点的数量小于2，无法进行线性回归";
                return decimal.MinValue;
            }
            //求出横纵坐标的平均值
            decimal averagex = 0, averagey = 0;
            foreach (Point p in parray)
            {
                averagex += p.X;
                averagey += p.Y;
            }
            averagex /= parray.Count;
            averagey /= parray.Count;
            //经验回归系数的分子与分母
            decimal numerator = 0;
            decimal denominator = 0;
            foreach (Point p in parray)
            {
                numerator += (p.X - averagex) * (p.Y - averagey);
                denominator += (p.X - averagex) * (p.X - averagex);
            }
            //回归系数b（Regression Coefficient）
            decimal RCB = numerator / denominator;
            //回归系数a
            decimal RCA = averagey - RCB * averagex;


            txt += "回归系数A： " + RCA.ToString("0.000\r\n");
            txt += "回归系数B： " + RCB.ToString("0.000\r\n");
            txt += string.Format("方程为： y ={1}*x + {0}", RCA.ToString("0.000"), RCB.ToString("0.000\r\n"));

            //剩余平方和与回归平方和
            decimal residualSS = 0;  //（Residual Sum of Squares）
            decimal regressionSS = 0; //（Regression Sum of Squares）
            foreach (Point p in parray)
            {
                residualSS +=
                  (p.Y - RCA - RCB * p.X) *
                  (p.Y - RCA - RCB * p.X);
                regressionSS +=
                  (RCA + RCB * p.X - averagey) *
                  (RCA + RCB * p.X - averagey);
            }

            txt += "剩余平方和： " + residualSS.ToString("0.000\r\n");
            txt += "回归平方和： " + regressionSS.ToString("0.000\r\n");

            return RCB;
        }

    }
}
